/********************************************************************************
 * Copyright (c) 2019-2020 [Open Lowcode SAS](https://openlowcode.com/)
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0 .
 *
 * SPDX-License-Identifier: EPL-2.0
 ********************************************************************************/

package org.openlowcode.server.data.properties;

import org.openlowcode.server.data.DataObject;
import org.openlowcode.server.data.DataObjectDefinition;
import org.openlowcode.server.runtime.OLcServer;
import org.openlowcode.server.runtime.SModule;
import org.openlowcode.tools.structure.ObjectDataElt;
import org.openlowcode.tools.structure.ObjectMasterIdDataElt;

/**
 * a data object master id encapsulates the unique text id for the set of
 * version of an object generated by the system. The different versions of the
 * same object will have different DataObjectId but the same DataObjectMasterId
 * 
 * @author <a href="https://openlowcode.com/" rel="nofollow">Open Lowcode
 *         SAS</a>
 *
 * @param <E> type of data object the data object master id is on
 */
public class DataObjectMasterId<E extends DataObject<E>> {
	public static <F extends DataObject<F>> DataObjectMasterId<F> getNullDataObjectMasterId(
			DataObjectDefinition<F> definition) {
		return new DataObjectMasterId<F>(null, definition);
	}

	private String id;
	private DataObjectDefinition<E> definition;

	/**
	 * get the master id as a string
	 * 
	 * @return master id as a string
	 */
	public String getId() {
		return this.id;
	}

	/**
	 * set the text id of the master id
	 * 
	 * @param id master id as a string
	 */
	void setId(String id) {
		this.id = id;
	}

	/**
	 * 
	 * can only be called from the org.openlowcode.server.data.properties package
	 * 
	 * @param id the string encapsulated in the object
	 */
	DataObjectMasterId(String id, DataObjectDefinition<E> definition) {
		this.id = id;
		this.definition = definition;
	}

	/**
	 * generates a data object master id, given the name of the module and the name
	 * of the data object. This method will check if the data object exists
	 * 
	 * @param id       id of this specific data object
	 * @param objectid a string identification of the object, should be
	 *                 'modulename:actionname'
	 * @return the data object master id
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public static DataObjectMasterId<?> generateDataObjectMasterId(String id, String objectid) {
		String[] splitobjectid = objectid.split(":");
		if (splitobjectid.length != 2)
			throw new RuntimeException(
					"objectid should have two components separated by ':', but does not have  " + objectid);
		String modulename = splitobjectid[0];
		SModule module = OLcServer.getServer().getModuleByName(modulename);
		String objectname = splitobjectid[1];
		DataObjectDefinition<?> objectdefinition = module.getObjectDefinition(objectname);
		return new DataObjectMasterId(id, objectdefinition);
	}

	/**
	 * casts the data object master id according to the given object definition
	 * 
	 * @param genericid        an uncasted data object master id
	 * @param objectdefinition definition of a data object
	 * @return casted data object master id
	 */
	public static <E extends DataObject<E>> DataObjectMasterId<E> castDataObjectMasterId(
			DataObjectMasterId<?> genericid,
			DataObjectDefinition<E> objectdefinition) {
		String objetdefinitionid = objectdefinition.getModuleName() + ":" + objectdefinition.getName();

		if (!objetdefinitionid.equals(genericid.getObjectId()))
			throw new RuntimeException("No consistency between master id internal class " + genericid.getObjectId()
					+ " and provided object id " + objetdefinitionid);
		return new DataObjectMasterId<E>(genericid.id, objectdefinition);
	}

	/**
	 * generates a master id from an object data element
	 * 
	 * @param element    object data element
	 * @param definition definition of the object
	 * @return the generated master id
	 */
	public static <E extends DataObject<E>> DataObjectMasterId<E> generatefromDataObjectElt(
			ObjectDataElt element,
			DataObjectDefinition<E> definition) {
		return new DataObjectMasterId<E>(element.getUID(), definition);
	}

	public static <E extends DataObject<E>> DataObjectMasterId<E> generatefromDataObjectMasterIdElt(ObjectMasterIdDataElt element,
			DataObjectDefinition<E> definition) {

		return new DataObjectMasterId<E>(element.getId(), definition);

	}
	
	@Override
	public String toString() {

		return "[DATAOBJECTID " + id + " " + this.getClass().getName() + "]";
	}

	@Override
	public boolean equals(Object otherobject) {
		if (otherobject instanceof DataObjectMasterId) {
			DataObjectMasterId<?> otherid = (DataObjectMasterId<?>) otherobject;
			if (otherid.getId()==null) {
				if (this.id==null) return true;
				return false;
			}
			return otherid.getId().equals(this.id);
		}
		return false;
	}

	@Override
	public int hashCode() {

		return (id!=null?this.id.hashCode():"".hashCode());
	}

	/**
	 * @return a string with module name, semi column and data object definition
	 *         name
	 */
	public String getObjectId() {
		return definition.getModuleName() + ":" + definition.getName();
	}
	

	/**
	 * allows to get the object from the id in algorithms that are common to several
	 * object types
	 * 
	 * @return the object after it has been lookup up
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public E lookupObject() {
		String[] splitobjectid = this.getObjectId().split(":");
		if (splitobjectid.length != 2)
			throw new RuntimeException(
					"objectid should have two components separated by ':', but does not have  " + this.getObjectId());
		String modulename = splitobjectid[0];
		SModule module = OLcServer.getServer().getModuleByName(modulename);
		return (E) (module.getDataObjectBasedOnGenericMasterId((DataObjectMasterId) this));
	}
}
